/*
 * Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
/*
 * The Apache Software License, Version 1.1
 *
 *
 * Copyright (c) 1999-2003 The Apache Software Foundation.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Xerces" and "Apache Software Foundation" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation and was
 * originally based on software copyright (c) 2002, International
 * Business Machines, Inc., http://www.apache.org.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

package com.sun.org.apache.xerces.internal.impl.dtd;

import com.sun.org.apache.xerces.internal.impl.XMLErrorReporter;
import com.sun.org.apache.xerces.internal.impl.msg.XMLMessageFormatter;
import com.sun.org.apache.xerces.internal.util.XMLSymbols;
import com.sun.org.apache.xerces.internal.xni.Augmentations;
import com.sun.org.apache.xerces.internal.xni.NamespaceContext;
import com.sun.org.apache.xerces.internal.xni.QName;
import com.sun.org.apache.xerces.internal.xni.XMLAttributes;
import com.sun.org.apache.xerces.internal.xni.XNIException;

/**
 * The DTD validator. The validator implements a document
 * filter: receiving document events from the scanner; validating
 * the content and structure; augmenting the InfoSet, if applicable;
 * and notifying the parser of the information resulting from the
 * validation process.
 * <p> Formerly, this component also handled DTD events and grammar construction.
 * To facilitate the development of a meaningful DTD grammar caching/preparsing
 * framework, this functionality has been moved into the XMLDTDLoader
 * class.  Therefore, this class no longer implements the DTDFilter
 * or DTDContentModelFilter interfaces.
 * <p>
 * This component requires the following features and properties from the
 * component manager that uses it:
 * <ul>
 * <li>http://xml.org/sax/features/namespaces</li>
 * <li>http://xml.org/sax/features/validation</li>
 * <li>http://apache.org/xml/features/validation/dynamic</li>
 * <li>http://apache.org/xml/properties/internal/symbol-table</li>
 * <li>http://apache.org/xml/properties/internal/error-reporter</li>
 * <li>http://apache.org/xml/properties/internal/grammar-pool</li>
 * <li>http://apache.org/xml/properties/internal/datatype-validator-factory</li>
 * </ul>
 *
 * @author Elena Litani, IBM
 * @author Michael Glavassevich, IBM
 * @xerces.internal
 */
public class XML11NSDTDValidator extends XML11DTDValidator {

  /**
   * Attribute QName.
   */
  private QName fAttributeQName = new QName();

  /**
   * Bind namespaces
   */
  protected final void startNamespaceScope(QName element, XMLAttributes attributes,
      Augmentations augs)
      throws XNIException {

    // add new namespace context
    fNamespaceContext.pushContext();

    if (element.prefix == XMLSymbols.PREFIX_XMLNS) {
      fErrorReporter.reportError(
          XMLMessageFormatter.XMLNS_DOMAIN,
          "ElementXMLNSPrefix",
          new Object[]{element.rawname},
          XMLErrorReporter.SEVERITY_FATAL_ERROR);
    }

    // search for new namespace bindings
    int length = attributes.getLength();
    for (int i = 0; i < length; i++) {
      String localpart = attributes.getLocalName(i);
      String prefix = attributes.getPrefix(i);
      // when it's of form xmlns="..." or xmlns:prefix="...",
      // it's a namespace declaration. but prefix:xmlns="..." isn't.
      if (prefix == XMLSymbols.PREFIX_XMLNS || prefix == XMLSymbols.EMPTY_STRING
          && localpart == XMLSymbols.PREFIX_XMLNS) {

        // get the internalized value of this attribute
        String uri = fSymbolTable.addSymbol(attributes.getValue(i));

        // 1. "xmlns" can't be bound to any namespace
        if (prefix == XMLSymbols.PREFIX_XMLNS && localpart == XMLSymbols.PREFIX_XMLNS) {
          fErrorReporter.reportError(
              XMLMessageFormatter.XMLNS_DOMAIN,
              "CantBindXMLNS",
              new Object[]{attributes.getQName(i)},
              XMLErrorReporter.SEVERITY_FATAL_ERROR);
        }

        // 2. the namespace for "xmlns" can't be bound to any prefix
        if (uri == NamespaceContext.XMLNS_URI) {
          fErrorReporter.reportError(
              XMLMessageFormatter.XMLNS_DOMAIN,
              "CantBindXMLNS",
              new Object[]{attributes.getQName(i)},
              XMLErrorReporter.SEVERITY_FATAL_ERROR);
        }

        // 3. "xml" can't be bound to any other namespace than it's own
        if (localpart == XMLSymbols.PREFIX_XML) {
          if (uri != NamespaceContext.XML_URI) {
            fErrorReporter.reportError(
                XMLMessageFormatter.XMLNS_DOMAIN,
                "CantBindXML",
                new Object[]{attributes.getQName(i)},
                XMLErrorReporter.SEVERITY_FATAL_ERROR);
          }
        }
        // 4. the namespace for "xml" can't be bound to any other prefix
        else {
          if (uri == NamespaceContext.XML_URI) {
            fErrorReporter.reportError(
                XMLMessageFormatter.XMLNS_DOMAIN,
                "CantBindXML",
                new Object[]{attributes.getQName(i)},
                XMLErrorReporter.SEVERITY_FATAL_ERROR);
          }
        }

        prefix = localpart != XMLSymbols.PREFIX_XMLNS ? localpart : XMLSymbols.EMPTY_STRING;

        // Declare prefix in context. Removing the association between a prefix and a
        // namespace name is permitted in XML 1.1, so if the uri value is the empty string,
        // the prefix is being unbound. -- mrglavas
        fNamespaceContext.declarePrefix(prefix, uri.length() != 0 ? uri : null);
      }
    }

    // bind the element
    String prefix = element.prefix != null ? element.prefix : XMLSymbols.EMPTY_STRING;
    element.uri = fNamespaceContext.getURI(prefix);
    if (element.prefix == null && element.uri != null) {
      element.prefix = XMLSymbols.EMPTY_STRING;
    }
    if (element.prefix != null && element.uri == null) {
      fErrorReporter.reportError(
          XMLMessageFormatter.XMLNS_DOMAIN,
          "ElementPrefixUnbound",
          new Object[]{element.prefix, element.rawname},
          XMLErrorReporter.SEVERITY_FATAL_ERROR);
    }

    // bind the attributes
    for (int i = 0; i < length; i++) {
      attributes.getName(i, fAttributeQName);
      String aprefix =
          fAttributeQName.prefix != null ? fAttributeQName.prefix : XMLSymbols.EMPTY_STRING;
      String arawname = fAttributeQName.rawname;
      if (arawname == XMLSymbols.PREFIX_XMLNS) {
        fAttributeQName.uri = fNamespaceContext.getURI(XMLSymbols.PREFIX_XMLNS);
        attributes.setName(i, fAttributeQName);
      } else if (aprefix != XMLSymbols.EMPTY_STRING) {
        fAttributeQName.uri = fNamespaceContext.getURI(aprefix);
        if (fAttributeQName.uri == null) {
          fErrorReporter.reportError(
              XMLMessageFormatter.XMLNS_DOMAIN,
              "AttributePrefixUnbound",
              new Object[]{element.rawname, arawname, aprefix},
              XMLErrorReporter.SEVERITY_FATAL_ERROR);
        }
        attributes.setName(i, fAttributeQName);
      }
    }

    // verify that duplicate attributes don't exist
    // Example: <foo xmlns:a='NS' xmlns:b='NS' a:attr='v1' b:attr='v2'/>
    int attrCount = attributes.getLength();
    for (int i = 0; i < attrCount - 1; i++) {
      String auri = attributes.getURI(i);
      if (auri == null || auri == NamespaceContext.XMLNS_URI) {
        continue;
      }
      String alocalpart = attributes.getLocalName(i);
      for (int j = i + 1; j < attrCount; j++) {
        String blocalpart = attributes.getLocalName(j);
        String buri = attributes.getURI(j);
        if (alocalpart == blocalpart && auri == buri) {
          fErrorReporter.reportError(
              XMLMessageFormatter.XMLNS_DOMAIN,
              "AttributeNSNotUnique",
              new Object[]{element.rawname, alocalpart, auri},
              XMLErrorReporter.SEVERITY_FATAL_ERROR);
        }
      }
    }

  } // startNamespaceScope(QName,XMLAttributes)

  /**
   * Handles end element.
   */
  protected void endNamespaceScope(QName element, Augmentations augs, boolean isEmpty)
      throws XNIException {

    // bind element
    String eprefix = element.prefix != null ? element.prefix : XMLSymbols.EMPTY_STRING;
    element.uri = fNamespaceContext.getURI(eprefix);
    if (element.uri != null) {
      element.prefix = eprefix;
    }

    // call handlers
    if (fDocumentHandler != null) {
      if (!isEmpty) {
        fDocumentHandler.endElement(element, augs);
      }
    }

    // pop context
    fNamespaceContext.popContext();

  } // endNamespaceScope(QName,boolean)
}
